<?php

//returns an array of in-context strings given article titles and full text
function getContext($array_links, $text)
{
	//remove formatting characters
	$array_replacements = array('/{{(.*)}}/', '/\[\[/', '/\]\]/');
	$text = preg_replace($array_replacements, '', $text);

	//parse array of section titles and associated positions within text 
	$array_sections = array();
	preg_match_all('/==(.*)==(.*)/U', $text, $array_matches);
	foreach ($array_matches[1] as $index => $section)
	{
		$array_sections[trim($section)] = strpos($text, $array_matches[0][$index]);
	}
	
	//get contextual substring of each link
	$pos = 0;
	$section = null;
	$context = null;
	$array_return = array();
	foreach ($array_links as $title)
	{
		$pos = stripos($text, $title);
		//$section = getSection($pos, $array_sections);
//		$title = str_replace('/', '\/', $title);
		//preg_match("/.*$title.*/i", $text, $array_matches);
		//if (isset($array_matches[0]))
		//	$context = $array_matches[0];
		//else
		//	$context = null;
		if ($pos)
		{
			$start = $end = $pos;
			//find beginning of sentence
			while ($text[$start] != '.' && $text[$start] != "\n" && $start >= 0)
				$start--;
			if ($text[$start] == '.')
				$start++;	//...move up to beginning of current sentence after detecting end of previous sentence
					
			//find end of sentence
			while ($text[$end] != '.' && $text[$end] != "\n" && $end < strlen($text))
				$end++;
			$end++;	//...include end of sentence if period
	
			$context = substr($text, $start, $end - $start);	//pull substring quote
			$context = trim($context);	//trim extraneous whitespace
			$context = str_ireplace($title, "<b>$title</b>", $context);	//bold matched term
		}
		else
			$context = null;
		$array_return[] = array($title, $context);
	}
	
	//print_r($array_return);

	return $array_return;

}

function getSection($pos, $array_sections)
{
	foreach ($array_sections as $section => $start)
		if ($pos > $start)
			return $section;

	return null;
}

//given article title, return plaintext contents from Wikipedia and update cache
function getArticle($resource_local, $title)
{
	$contents = null;
	
	if ($contents = checkArticleCache($resource_local, $title))
		return $contents;
	
	$url = "http://en.wikipedia.org/wiki/".rawurlencode($title)."?action=raw";

	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_ENCODING, "UTF-8");
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");

	$contents = curl_exec($ch);

	cacheArticle($resource_local, $title, $contents);

	return $contents;
}

function multiCurlExec($array_urls)
{
	$multi = curl_multi_init();
	$array_handles = array();
	$array_content = array();

	foreach ($array_urls as $url)
	{
		$array_handles[$url] = curl_init();
		curl_setopt($array_handles[$url], CURLOPT_URL, $url);
		curl_setopt($array_handles[$url], CURLOPT_ENCODING, "UTF-8");
		curl_setopt($array_handles[$url], CURLOPT_RETURNTRANSFER, true);
		curl_setopt($array_handles[$url], CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
		curl_multi_add_handle($multi, $array_handles[$url]);
	}

	$running = 0;
	do
	{
		curl_multi_exec($multi, $running);
	}
	while ($running > 0);
		
	foreach ($array_handles as $index => $handle)
	{
		$array_content[$index] = curl_multi_getcontent($handle);
		curl_multi_remove_handle($multi, $handle);
	}

	curl_multi_close($multi);

	return $array_content;
}

//primary function
//given resource and search term, return multimentional array of terms and in-context quotes to render to user
function get_related_terms($resource_local, $search, &$cache_hits, $kiwibot = false)
{
	//set up function variables
	$array_results = array();
	$disambiguation = false;
	$db_mode = false;
	$normalized = null;

	//if database connection successful
	//fetch known related terms from database cache
	if ($resource_local)
	{	
		//normalize search to match wikipedia term
		$search_safe = mysql_real_escape_string($search);
		list($normalized) = mysql_fetch_array(mysql_log($resource_local, "SELECT redirect FROM redirects WHERE original = '$search_safe';"));
		if (isset($normalized))
			$search = $normalized;

		if (!$kiwibot)
			$array_results = check_cache($resource_local, $search, $disambiguation);
	}

	//call wikipedia api if term doesn't exist in local cache
	if ($kiwibot || !count($array_results))
	{
		loginWikipedia();
		
		$url = 'http://en.wikipedia.org/w/api.php';
		$post = "action=query&titles=$search&prop=links&pllimit=500&redirects=true&format=xml";
	
	        $ch = curl_init();
	        curl_setopt($ch, CURLOPT_URL, ($url));
	        curl_setopt($ch, CURLOPT_ENCODING, "UTF-8");
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//	        curl_setopt($ch, CURLOPT_COOKIEFILE, $settings['cookiefile']);
//	        curl_setopt($ch, CURLOPT_COOKIEJAR, $settings['cookiefile']);
	        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
	        
		if (!empty($post)) curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
	        //UNCOMMENT TO DEBUG TO output.tmp
//	        curl_setopt($ch, CURLOPT_VERBOSE, true); // Display communication with server
//	        $fp = fopen("output.tmp", "w");
//	        curl_setopt($ch, CURLOPT_STDERR, $fp); // Display communication with server
       	 
		$xml = curl_exec($ch);
		
		if (!$xml) {
		throw new Exception("Error getting data from server ($url): " . curl_error($ch));
		}
		
		curl_close($ch);
		
		preg_match_all('/title="(.*)"/U', $xml, $array_matches);

		$array_results = $array_matches[1];

		//override search term with Wikipedia's returned search
		//example: ucla -> University of California, Los Angeles
		//if terms are different, cache redirect
		if ($search != $array_results[0])
			addRedirect($resource_local, $search, $array_results[0]);
		$normalized = $array_results[0];

		//cache relationships
		if ($resource_local)		
			cache_related($resource_local, $normalized, $array_results);

		if ($kiwibot)
			return $array_results;
	}
	
	$array_filtered = array();

	//filter out most relevant terms using heuristic algorithm
	$array_filtered = heuristic_algorithm($resource_local, $normalized, $array_results, $cache_hits);
	
	//pass array of titles and get associated in-context quotes
	$array_return = getContext($array_filtered, getArticle($resource_local, $normalized));

	return $array_return;
}

function check_cache($resource_local, $search, &$disambiguation)
{
	$array_results = array();

	$resource = mysql_log($resource_local, "SELECT article FROM titles WHERE id IN (SELECT term2 FROM relationships WHERE term1 = '$search');");
	while (list($term) = mysql_fetch_array($resource))
	{	
		if ($term == "HELP:DISAMBIGUATION")
			$disambiguation = true;
		$array_results[] = $term;
	}

	return $array_results;
}

function heuristic_algorithm($resource_local, $search, $array_articles, &$cache_hits)
{
	$array_articles_scored = array();
	$array_strongly_related = array();

	if( $resource_local ) {
	//build list of strongly-related terms
	$search = mysql_real_escape_string($search);
	$resource = mysql_log($resource_local, "SELECT article FROM titles WHERE id IN (SELECT a.term2 FROM relationships a, relationships b WHERE a.term1 != b.term1 AND a.term1 = b.term2 AND a.term2 = b.term1 AND a.term1 IN (SELECT id FROM titles WHERE article = '$search'));");
	$array_strongly_related = array();
	while(list($article) = mysql_fetch_array($resource))
		$array_strongly_related[] = $article;
	}

	//assign scores to each article
	foreach ($array_articles as $article)
	{
		$array_articles_scored[$article] = 0;
	
		//3 points for strongly-related term
		if (in_array($article, $array_strongly_related))
		{
			$array_articles_scored[$article] += 3;
			$cache_hits++;
		}

		//1 point for substring match 
		if (stripos($article, $search))
			$array_articles_scored[$article]++;
	}

	//sort articles by descending score
	arsort($array_articles_scored);

	$array_results_scored = $array_results_scoreless = array();

	//divide array into scored and scoreless
	foreach ($array_articles_scored as $article => $score)
	{
		if ($score > 0)
			$array_results_scored[] = $article;
		else
			$array_results_scoreless[] = $article;
	}

	//return all scored articles
	if (count($array_results_scored) >= 20)
	{
		return $array_results_scored;
	}
	else	//return up to 20 random articles including scored articles
	{	
		shuffle($array_results_scoreless);
		return array_merge($array_results_scored, array_slice($array_results_scoreless, 0, 20 - count($array_results_scored)));
	}
}

//cache ids of article and related term
function cache_related($resource_local, $term, $array_related)
{
	$affected = 0;

	//get id of term1
	$term1 = getArticleId($resource_local, $term);

	foreach ($array_related as $related)
	{
		$term2 = getArticleId($resource_local, $related);

		mysql_log($resource_local, "INSERT IGNORE INTO relationships VALUES ($term1, $term2);");
	
		$affected += mysql_affected_rows();
	}

	return $affected;
}

function loginWikipedia()
{
	try {
		global $settings;
		$settings['wikiroot'] = "http://cablegatewiki.org";
		$settings['cookiefile'] = "cookies.tmp";
		$token = login($settings['wikiuser'], $settings['wikipass']);
//		login($settings['user'], $settings['pass'], $token);
        } catch (Exception $e) {
                die("FAILED: " . $e->getMessage());
        }
}

function httpRequest($url, $post="") {
        global $settings;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, ($url));
        curl_setopt($ch, CURLOPT_ENCODING, "UTF-8");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_COOKIEFILE, $settings['cookiefile']);
        curl_setopt($ch, CURLOPT_COOKIEJAR, $settings['cookiefile']);

        if (!empty($post)) curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
        //UNCOMMENT TO DEBUG TO output.tmp
//        curl_setopt($ch, CURLOPT_VERBOSE, true); // Display communication with server
//        $fp = fopen("output.tmp", "w");
//        curl_setopt($ch, CURLOPT_STDERR, $fp); // Display communication with server
        
        $xml = curl_exec($ch);
        
        if (!$xml) {
                throw new Exception("Error getting data from server ($url): " . curl_error($ch));
        }

        curl_close($ch);
        
        return $xml;
}


function login ($user, $pass, $token='') {
        global $settings;

        $url = $settings['wikiroot'] . "/api.php?action=login&format=xml";



        $params = "action=login&lgname=$user&lgpassword=$pass";
        if (!empty($token)) {
                $params .= "&lgtoken=$token";
        }

        $data = httpRequest($url, $params);
        
        if (empty($data)) {
                throw new Exception("No data received from server. Check that API is enabled.");
        }

        $xml = simplexml_load_string($data);
        //checkForErrors($xml);

        if (!empty($token)) {
                //Check for successful login
                $expr = "/api/login[@result='Success']";
                $result = $xml->xpath($expr);

                if(!count($result)) {
                        throw new Exception("Login failed");
                }
        } else {
                $expr = "/api/login[@token]";
                $result = $xml->xpath($expr);

                if(!count($result)) {
                        throw new Exception("Login token not found in XML");
                }
        }
        
        return $result[0]->attributes()->token;
}

?>
